package cn.always.xiajia.framework.common.mybatisplus.util;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.service.IService;

import cn.always.xiajia.framework.common.mybatisplus.lang.FkField;
import cn.always.xiajia.framework.common.util.ExtBeanCopyUtils;
import cn.always.xiajia.framework.common.util.ExtEntityUtils;
import cn.always.xiajia.framework.common.util.ExtGuavaUtils;
import cn.always.xiajia.framework.common.util.ExtRefUtils;
import cn.always.xiajia.framework.common.util.ExtSpringUtils;

/**
 * a、一个主单设值另外一个明细属性（1-1） b、一个主单设值另外一个明细对象（1-1） c、一个主单设值另外一个明细list对象（1-多)
 * d、一个主单设值另外一个明细list属性（1-多）
 * 
 * @author xgj
 *
 */
public class ExtFieldInjectUtils {

	private ExtFieldInjectUtils() {

	}

	// -ab-V 单个--

	/**
	 * <p>
	 * 适用于一对一查询VO属性注入 a
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO 需要实现{@link FkField}
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param serviceClazz 副表对应的IService实现类Class对象
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectField(V data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, SFunction<V, W>... injectColumns) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表外键值
		R id = ExtEntityUtils.toObj(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (id != null && injectColumns.length > 0) {
			List<String> injectFieldNames = ExtRefUtils.getFiledNames(injectColumns);
			String[] selectField = { String.join(",", injectFieldNames) };
			// 副表主键查询数据
			S s = iService.getBaseMapper().selectById(id);
			// 使用Spring内置的属性复制工具类
			ExtBeanCopyUtils.copyProperties(s, data, selectField);
		}
	}

	/**
	 * <p>
	 * 适用于一对一查询VO属性注入 a
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectField(V data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn, SFunction<V, W>... injectColumns) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表关联外键的集合（去重）
		R id = ExtEntityUtils.toObj(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (id != null && injectColumns.length > 0) {
			// 主表关联外键对应的字段名字符串
			String pkfieldName = ExtRefUtils.getFiledName(pkColumn);
			List<String> injectFieldNames = ExtRefUtils.getFiledNames(injectColumns);
			String[] selectField = { pkfieldName, String.join(",", injectFieldNames) };
			// 数据库查询字段需要下划线表示
			String selectStr = Arrays.stream(selectField).map(ExtGuavaUtils::toUnderScoreCase).collect(Collectors.joining(","));
			// 构造副表查询条件(查询指定列元素)
			QueryWrapper<S> wrapper = Wrappers.query(ExtRefUtils.newInstance(iService.getEntityClass())).select(selectStr)
					.eq(ExtGuavaUtils.toUnderScoreCase(pkfieldName), id);
			// 通过主表的外键查询关联副表符合条件的数据
			S one = iService.getBaseMapper().selectOne(wrapper);
			// 使用Spring内置的属性复制方法
			ExtBeanCopyUtils.copyProperties(one, data, injectFieldNames);
		}
	}

	/**
	 * <p>
	 * 适用于一对一查询VO对象注入 b
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO 需要实现{@link FkField}
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param serviceClazz 副表对应的IService实现类Class对象
	 * @param injectColumn 需要注入的列（字段） 方法引用表示
	 * @param <W> 需要注入属性的列泛型
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectEntity(V data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, SFunction<V, W> injectColumn) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表外键值
		R id = ExtEntityUtils.toObj(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (id != null) {
			// 副表主键查询数据
			S s = iService.getBaseMapper().selectById(id);
			String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
			// 使用反射给属性赋值
			ExtRefUtils.setFiledValue(data, injectFieldName, s);
		}
	}

	/**
	 * <p>
	 * 适用于一对一查询VO对象注入 b
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectEntity(V data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn, SFunction<V, W> injectColumn) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表关联外键的集合（去重）
		R id = ExtEntityUtils.toObj(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (id != null) {
			// 构造副表查询条件(查询指定列元素)
			LambdaQueryWrapper<S> wrapper = Wrappers.lambdaQuery(iService.getEntityClass()).eq(pkColumn, id);
			// 通过主表的外键查询关联副表符合条件的数据
			S one = iService.getBaseMapper().selectOne(wrapper);
			String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
			// 使用Spring内置的属性复制方法
			ExtRefUtils.setFiledValue(data, injectFieldName, one);
		}
	}

	// -ab-V list--

	/**
	 * <p>
	 * 适用于一对一查询VO属性注入 a
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectField(List<V> data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn, SFunction<V, W>... injectColumns) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表关联外键的集合（去重）
		Set<? extends R> ids = ExtEntityUtils.toSet(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (ids.size() > 0 && injectColumns.length > 0) {
			// 主表关联外键对应的字段名字符串
			String pkfieldName = ExtRefUtils.getFiledName(pkColumn);
			List<String> injectFieldNames = ExtRefUtils.getFiledNames(injectColumns);
			String[] selectField = { pkfieldName, String.join(",", injectFieldNames) };
			// 数据库查询字段需要下划线表示
			String selectStr = Arrays.stream(selectField).map(ExtGuavaUtils::toUnderScoreCase).collect(Collectors.joining(","));
			// 构造副表查询条件(查询指定列元素)
			QueryWrapper<S> wrapper = Wrappers.query(ExtRefUtils.newInstance(iService.getEntityClass())).select(selectStr)
					.in(ExtGuavaUtils.toUnderScoreCase(pkfieldName), ids);
			// 通过主表的外键查询关联副表符合条件的数据
			List<S> list = iService.getBaseMapper().selectList(wrapper);
			// 将list转换为map 其中Key为副表主键 Value为副表类型实例本身
			Map<R, S> map = ExtEntityUtils.toMap(list, pkColumn, e -> e);
			String fkfieldName = ExtRefUtils.getFiledName(fkColumn);
			doInjectField(data, fkfieldName, map, ExtRefUtils.getFiledNames(injectColumns));
		}
	}

	/**
	 * <p>
	 * 适用于一对一查询VO属性注入(不查询) a
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param sData 副表对应的VO实例
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectFieldData(List<V> vData, final SFunction<V, R> fkColumn,
			List<S> sData, final SFunction<S, R> pkColumn, SFunction<V, W>... injectColumns) {
		if (vData != null && vData.size() > 0 && sData != null && sData.size() > 0 && injectColumns.length > 0) {
			// 将list转换为map 其中Key为副表主键 Value为副表类型实例本身
			Map<R, S> map = ExtEntityUtils.toMap(sData, pkColumn, e -> e);
			String fkfieldName = ExtRefUtils.getFiledName(fkColumn);
			doInjectField(vData, fkfieldName, map, ExtRefUtils.getFiledNames(injectColumns));
		}
	}

	/**
	 * <p>
	 * 适用于一对一查询VO属性注入 a
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectFieldPage(IPage<V> page, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn, SFunction<V, W>... injectColumns) {
		List<V> records = page.getRecords();
		injectField(records, fkColumn, clazz, pkColumn, injectColumns);
	}

	/**
	 * <p>
	 * 适用于一对一查询VO属性注入(不查询) a
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param sData 副表对应的VO实例
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectFieldDataPage(IPage<V> page,
			final SFunction<V, R> fkColumn, List<S> sData, final SFunction<S, R> pkColumn, SFunction<V, W>... injectColumns) {
		List<V> records = page.getRecords();
		injectFieldData(records, fkColumn, sData, pkColumn, injectColumns);
	}

	/**
	 * <p>
	 * 适用于一对一查询VO对象注入 b
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectEntity(List<V> data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn, SFunction<V, W> injectColumn) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表关联外键的集合（去重）
		Set<? extends R> ids = ExtEntityUtils.toSet(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (ids.size() > 0) {
			// 主表关联外键对应的字段名字符串
			String fkfieldName = ExtRefUtils.getFiledName(fkColumn);
			// 构造副表查询条件(查询指定列元素)
			LambdaQueryWrapper<S> wrapper = Wrappers.lambdaQuery(iService.getEntityClass()).in(pkColumn, ids);
			// 通过主表的外键查询关联副表符合条件的数据
			List<S> list = iService.getBaseMapper().selectList(wrapper);
			String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
			// 将list转换为map 其中Key为副表主键 Value为副表类型实例本身
			Map<R, S> map = ExtEntityUtils.toMap(list, pkColumn, e -> e);
			doInjectFieldEntity(data, fkfieldName, map, injectFieldName);
		}
	}

	/**
	 * <p>
	 * 适用于一对一查询VO对象注入(不查询) b
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param vData 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param sData 副表对应的VO实例
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectEntityData(List<V> vData,
			final SFunction<V, R> fkColumn, List<S> sData, final SFunction<S, R> pkColumn, SFunction<V, W> injectColumn) {
		if (vData != null && vData.size() > 0 && sData != null && sData.size() > 0) {
			String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
			// 将list转换为map 其中Key为副表主键 Value为副表类型实例本身
			Map<R, S> map = ExtEntityUtils.toMap(sData, pkColumn, e -> e);
			String fkfieldName = ExtRefUtils.getFiledName(fkColumn);
			doInjectFieldEntity(vData, fkfieldName, map, injectFieldName);
		}
	}

	/**
	 * <p>
	 * 适用于一对一查询VO对象注入 b
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectEntityPage(IPage<V> page,
			final SFunction<V, R> fkColumn, Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn,
			SFunction<V, W> injectColumn) {
		List<V> records = page.getRecords();
		injectEntity(records, fkColumn, clazz, pkColumn, injectColumn);
	}

	/**
	 * <p>
	 * 适用于一对一查询VO对象注入(不查询) b
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param vData 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param sData 副表对应的VO实例
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectEntityDataPage(IPage<V> page,
			final SFunction<V, R> fkColumn, List<S> sData, final SFunction<S, R> pkColumn, SFunction<V, W> injectColumn) {
		List<V> records = page.getRecords();
		injectEntityData(records, fkColumn, sData, pkColumn, injectColumn);
	}

	// -cd-V 单个--

	/**
	 * <p>
	 * 适用于一对多查询VO对象注入 c
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectListEntity(V data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn, SFunction<V, W> injectColumn) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表关联外键的集合（去重）
		R id = ExtEntityUtils.toObj(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (id != null) {
			// 构造副表查询条件(查询指定列元素)
			LambdaQueryWrapper<S> wrapper = Wrappers.lambdaQuery(iService.getEntityClass()).eq(pkColumn, id);
			// 通过主表的外键查询关联副表符合条件的数据
			List<S> list = iService.getBaseMapper().selectList(wrapper);
			String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
			// 使用Spring内置的属性复制方法
			ExtRefUtils.setFiledValue(data, injectFieldName, list);
		}
	}

	/**
	 * <p>
	 * 适用于一对多查询VO属性注入 d
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectListField(V data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn, SFunction<S, W>... injectColumns) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表关联外键的集合（去重）
		R id = ExtEntityUtils.toObj(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (id != null && injectColumns.length > 0) {
			// 主表关联外键对应的字段名字符串
			String pkfieldName = ExtRefUtils.getFiledName(pkColumn);
			List<String> injectFieldNames = ExtRefUtils.getFiledNames(injectColumns);
			String[] selectField = { pkfieldName, String.join(",", injectFieldNames) };
			// 数据库查询字段需要下划线表示
			String selectStr = Arrays.stream(selectField).map(ExtGuavaUtils::toUnderScoreCase).collect(Collectors.joining(","));
			// 构造副表查询条件(查询指定列元素)
			QueryWrapper<S> wrapper = Wrappers.query(ExtRefUtils.newInstance(iService.getEntityClass())).select(selectStr)
					.eq(ExtGuavaUtils.toUnderScoreCase(pkfieldName), id);
			// 通过主表的外键查询关联副表符合条件的数据
			List<S> list = iService.getBaseMapper().selectList(wrapper);
			Map<String, List<W>> mapFields = ExtEntityUtils.toListMap(list, injectColumns);
			doInjectFieldList(data, mapFields);
		}
	}

	// -cd-V 多个--

	/**
	 * <p>
	 * 适用于一对多查询VO对象注入 c
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R, W> void injectListEntity(List<V> data, final SFunction<V, R> fkColumn, Class<? extends IService<S>> clazz,
			final SFunction<S, R> pkColumn, SFunction<V, W> injectColumn) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表关联外键的集合（去重）
		Set<? extends R> ids = ExtEntityUtils.toSet(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (ids.size() > 0) {
			// 构造副表查询条件(查询指定列元素)
			LambdaQueryWrapper<S> wrapper = Wrappers.lambdaQuery(iService.getEntityClass()).in(pkColumn, ids);
			// 通过主表的外键查询关联副表符合条件的数据
			List<S> list = iService.getBaseMapper().selectList(wrapper);
			String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
			// 将list转换为map 其中Key为副表主键 Value为副表类型实例本身
			Map<R, List<S>> map = list.stream().collect(Collectors.groupingBy(pkColumn));
			String fkfiledName = ExtRefUtils.getFiledName(fkColumn);
			doInjectField(data, fkfiledName, map, injectFieldName);
		}
	}

	/**
	 * <p>
	 * 适用于一对多查询VO对象注入(不查询) c
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param sData 副表对应的VO实例
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectListEntityData(List<V> vData,
			final SFunction<V, R> fkColumn, List<S> sData, final SFunction<S, R> pkColumn, SFunction<V, W> injectColumn) {
		if (vData != null && vData.size() > 0 && sData != null && sData.size() > 0) {
			String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
			// 将list转换为map 其中Key为副表主键 Value为副表类型实例本身
			Map<R, List<S>> map = sData.stream().collect(Collectors.groupingBy(pkColumn));
			String fkfiledName = ExtRefUtils.getFiledName(fkColumn);
			doInjectField(vData, fkfiledName, map, injectFieldName);
		}
	}

	/**
	 * <p>
	 * 适用于一对多查询VO对象注入 c
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectListEntityPage(IPage<V> page,
			final SFunction<V, R> fkColumn, Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn,
			SFunction<V, W> injectColumn) {
		List<V> records = page.getRecords();
		injectListEntity(records, fkColumn, clazz, pkColumn, injectColumn);
	}

	/**
	 * <p>
	 * 适用于一对多查询VO对象注入(不查询) c
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param sData 副表对应的VO实例
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	public static <V, S, R extends Serializable, W extends Serializable> void injectListEntityDataPage(IPage<V> page,
			final SFunction<V, R> fkColumn, List<S> sData, final SFunction<S, R> pkColumn, SFunction<V, W> injectColumn) {
		List<V> records = page.getRecords();
		injectListEntityData(records, fkColumn, sData, pkColumn, injectColumn);
	}

	/**
	 * <p>
	 * 适用于一对多查询VO属性注入 d
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectListField(List<V> data, final SFunction<V, R> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn, SFunction<S, W>... injectColumns) {
		IService<S> iService = ExtSpringUtils.getBean(clazz);
		// 获取主表关联外键的集合（去重）
		Set<? extends R> ids = ExtEntityUtils.toSet(data, fkColumn);
		// 如果集合元素个数不为空 则进行后续操作
		if (ids.size() > 0 && injectColumns.length > 0) {
			// 主表关联外键对应的字段名字符串
			String pkfieldName = ExtRefUtils.getFiledName(pkColumn);
			List<String> injectFieldNames = ExtRefUtils.getFiledNames(injectColumns);
			String[] selectField = { pkfieldName, String.join(",", injectFieldNames) };
			// 数据库查询字段需要下划线表示
			String selectStr = Arrays.stream(selectField).map(ExtGuavaUtils::toUnderScoreCase).collect(Collectors.joining(","));
			// 构造副表查询条件(查询指定列元素)
			QueryWrapper<S> wrapper = Wrappers.query(ExtRefUtils.newInstance(iService.getEntityClass())).select(selectStr)
					.in(ExtGuavaUtils.toUnderScoreCase(pkfieldName), ids);
			// 通过主表的外键查询关联副表符合条件的数据
			List<S> list = iService.getBaseMapper().selectList(wrapper);
			Map<R, List<S>> map = list.stream().collect(Collectors.groupingBy(pkColumn));
			Map<R, Map<String, List<W>>> mapPkFields = new HashMap<>();
			for (R r : map.keySet()) {
				mapPkFields.put(r, ExtEntityUtils.toListMap(map.get(r), injectColumns));
			}
			String fkfieldName = ExtRefUtils.getFiledName(fkColumn);
			doInjectFieldList(data, fkfieldName, mapPkFields);
		}
	}

	/**
	 * <p>
	 * 适用于一对多查询VO属性注入(不查询) d
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param sData 副表对应的VO实例
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectListFieldData(List<V> vData,
			final SFunction<V, R> fkColumn, List<S> sData, final SFunction<S, R> pkColumn, SFunction<S, W>... injectColumns) {
		if (vData != null && vData.size() > 0 && sData != null && sData.size() > 0) {
			// 主表关联外键对应的字段名字符串
			Map<R, List<S>> map = sData.stream().collect(Collectors.groupingBy(pkColumn));
			Map<R, Map<String, List<W>>> mapPkFields = new HashMap<>();
			for (R r : map.keySet()) {
				mapPkFields.put(r, ExtEntityUtils.toListMap(map.get(r), injectColumns));
			}
			String fkfieldName = ExtRefUtils.getFiledName(fkColumn);
			doInjectFieldList(vData, fkfieldName, mapPkFields);
		}
	}

	/**
	 * <p>
	 * 适用于一对多查询VO属性注入 d
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectListFieldPage(IPage<V> page,
			final SFunction<V, R> fkColumn, Class<? extends IService<S>> clazz, final SFunction<S, R> pkColumn,
			SFunction<S, W>... injectColumns) {
		List<V> records = page.getRecords();
		injectListField(records, fkColumn, clazz, pkColumn, injectColumns);
	}

	/**
	 * <p>
	 * 适用于一对多查询VO属性注入(不查询) d
	 * </p>
	 * <p>
	 * 通过反射的方式给关联表查询注入属性值 需要说明的是 相较于手动编码 反射的执行效率略微差点 本方法优点是能够提高开发效率 后续考虑逆向工程优化性能
	 * </p>
	 * <p>
	 * 主副表的定义：主表是指包含外键的表
	 * </p>
	 *
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 主表外键、副表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param sData 副表对应的VO实例
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumns 需要注入的列（字段） 方法引用表示
	 */
	@SafeVarargs
	public static <V, S, R extends Serializable, W extends Serializable> void injectListFieldDataPage(IPage<V> page,
			final SFunction<V, R> fkColumn, List<S> sData, final SFunction<S, R> pkColumn, SFunction<S, W>... injectColumns) {
		List<V> records = page.getRecords();
		injectListFieldData(records, fkColumn, sData, pkColumn, injectColumns);
	}

	// 通过当前对象的ids查询对应的list对象
	/**
	 * 通过当前对象的ids查询对应的list对象
	 * 
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 泛型
	 * @param <Z> 副表主键对应的数据类型
	 * @param <W> 主表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumn
	 */
	public static <V, S, R, Z extends Serializable, W> void injectEntityByList(V data, final SFunction<V, List<R>> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, Z> pkColumn, SFunction<V, W> injectColumn) {
		if (data != null) {
			IService<S> iService = ExtSpringUtils.getBean(clazz);
			List<R> ids = ExtRefUtils.getFieldValue(data, fkColumn);
			if (ids != null && ids.size() > 0) {
				// 构造副表查询条件(查询指定列元素)
				LambdaQueryWrapper<S> wrapper = Wrappers.lambdaQuery(iService.getEntityClass()).in(pkColumn, ids);
				// 通过主表的外键查询关联副表符合条件的数据
				List<S> list = iService.getBaseMapper().selectList(wrapper);
				String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
				// 使用反射给属性赋值
				ExtRefUtils.setFiledValue(data, injectFieldName, list);
			}
		}
	}

	/**
	 * 通过当前对象的ids查询对应的list对象
	 * 
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 泛型
	 * @param <Z> 副表主键对应的数据类型
	 * @param <W> 主表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumn
	 */
	public static <V, S, R, Z extends Serializable, W> void injectEntityByList(List<V> data, final SFunction<V, List<R>> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, Z> pkColumn, SFunction<V, W> injectColumn) {
		if (data != null && data.size() > 0) {
			Set<List<R>> ids = ExtEntityUtils.toSet(data, fkColumn);
			if (ids != null && ids.size() > 0) {
				Set<R> idsNew = new HashSet<>();
				ids.forEach(a -> {
					idsNew.addAll(a);
				});
				IService<S> iService = ExtSpringUtils.getBean(clazz);
				if (idsNew != null && idsNew.size() > 0) {
					// 构造副表查询条件(查询指定列元素)
					LambdaQueryWrapper<S> wrapper = Wrappers.lambdaQuery(iService.getEntityClass()).in(pkColumn, idsNew);
					// 通过主表的外键查询关联副表符合条件的数据
					List<S> list = iService.getBaseMapper().selectList(wrapper);
					Map<Z, List<S>> map = list.stream().collect(Collectors.groupingBy(pkColumn));
					String injectFieldName = ExtRefUtils.getFiledName(injectColumn);
					// 使用反射给属性赋值
					String fkfiledName = ExtRefUtils.getFiledName(fkColumn);
					doInjectFieldList(data, fkfiledName, map, injectFieldName);
				}
			}

		}
	}

	/**
	 * 通过当前对象的ids查询对应的list对象
	 * 
	 * @param <V> 主表对应的实体类VO
	 * @param <S> 副表对应的实体类DO
	 * @param <R> 泛型
	 * @param <Z> 副表主键对应的数据类型
	 * @param <W> 主表主键对应的数据类型
	 * @param data 主表对应的VO实例
	 * @param fkColumn 主表关联外键列（字段） 方法引用表示
	 * @param clazz 副表对应的IService实现类Class对象
	 * @param pkColumn 副表对应的主键（字段）方法引用表示
	 * @param injectColumn
	 */
	public static <V, S, R, Z extends Serializable, W> void injectEntityByListPage(IPage<V> page, final SFunction<V, List<R>> fkColumn,
			Class<? extends IService<S>> clazz, final SFunction<S, Z> pkColumn, SFunction<V, W> injectColumn) {
		List<V> records = page.getRecords();
		injectEntityByList(records, fkColumn, clazz, pkColumn, injectColumn);
	}

	// ----------------------------------------------------

	/**
	 * 具体执行属性复制逻辑 原理使用反射 确保仅复制指定字段 严格保证数据正确性
	 *
	 * @param data 含有空值属性的对象集合
	 * @param fieldName 主表关联外键字段名
	 * @param map 副表主键与当前对象Map
	 * @param filedNames 需要注入属性的字段（驼峰表示）
	 * @param <T> 主表VO泛型
	 * @param <S> 副表DO泛型
	 * @param <R> 主表关联外键、副表主键的类型泛型
	 */
	private static <T, S, R> void doInjectField(List<T> data, String fieldName, Map<R, S> map, List<String> filedNames) {
		if (fieldName != null && filedNames.size() > 0) {
			for (T t : data) {
				// 获取当前主表VO对象实例关联外键的值
				R r = ExtRefUtils.getFieldValue(t, fieldName);
				// 从map中取出关联副表的对象实例
				S s = map.get(r);
				// 使用Spring内置的属性复制方法
				ExtBeanCopyUtils.copyProperties(s, t, filedNames);
			}
		}
	}

	/**
	 * 批量赋值多个对象
	 * 
	 * @param <V>
	 * @param <S>
	 * @param <R>
	 * @param data
	 * @param pkFieldName
	 * @param map
	 * @param injectFieldName
	 */
	private static <V, S, R> void doInjectField(List<V> data, String pkFieldName, Map<R, List<S>> map, String injectFieldName) {
		if (pkFieldName != null && injectFieldName != null) {
			for (V v : data) {
				// 获取当前主表VO对象实例关联外键的值
				R r = ExtRefUtils.getFieldValue(v, pkFieldName);
				// 从map中取出关联副表的对象实例
				List<S> s = map.get(r);
				// 使用反射给属性赋值
				ExtRefUtils.setFiledValue(v, injectFieldName, s);
			}
		}
	}

	/**
	 * 批量赋值多个对象pk是list
	 * 
	 * @param <V>
	 * @param <S>
	 * @param <R>
	 * @param data
	 * @param pkFieldName
	 * @param map
	 * @param injectFieldName
	 */
	private static <V, S, R> void doInjectFieldList(List<V> data, String pkFieldName, Map<R, List<S>> map, String injectFieldName) {
		if (pkFieldName != null && injectFieldName != null) {
			List<S> ss = new ArrayList<>();
			for (V v : data) {
				ss.clear();
				// 获取当前主表VO对象实例关联外键的值
				List<R> r = ExtRefUtils.getFieldValue(v, pkFieldName);
				r.forEach(a -> {
					// 从map中取出关联副表的对象实例
					List<S> s = map.get(a);
					if (s != null && s.size() > 0) {
						ss.addAll(s);
					}
				});
				// 使用反射给属性赋值
				ExtRefUtils.setFiledValue(v, injectFieldName, ss);
			}
		}
	}

	/**
	 * 批量赋值单个对象
	 * 
	 * @param <V>
	 * @param <S>
	 * @param <R>
	 * @param data
	 * @param pkFieldName
	 * @param map
	 * @param injectFieldName
	 */
	private static <V, S, R> void doInjectFieldEntity(List<V> data, String pkFieldName, Map<R, S> map, String injectFieldName) {
		if (pkFieldName != null && injectFieldName != null) {
			for (V v : data) {
				// 获取当前主表VO对象实例关联外键的值
				R r = ExtRefUtils.getFieldValue(v, pkFieldName);
				// 从map中取出关联副表的对象实例
				S s = map.get(r);
				// 使用反射给属性赋值
				ExtRefUtils.setFiledValue(v, injectFieldName, s);
			}
		}
	}

	/**
	 * 批量赋值单个对象（赋值给属性名称+s）
	 * 
	 * @param <V> 主表
	 * @param <R> 外键
	 * @param <W> 泛型
	 * @param data
	 * @param mapFields
	 */
	private static <V, R, W extends Serializable> void doInjectFieldList(V data, Map<R, List<W>> mapFields) {
		if (mapFields != null && mapFields.size() > 0) {
			for (R r : mapFields.keySet()) {
				// 从map中取出关联副表的对象实例
				List<W> w = mapFields.get(r);
				// 使用反射给属性赋值
				ExtRefUtils.setFiledValue(data, r + "s", w);
			}
		}
	}

	/**
	 * 批量赋值单个对象（赋值给属性名称+s）
	 * 
	 * @param <V> 主表
	 * @param <R> 外键
	 * @param <Z> 字段名
	 * @param <W> 泛型
	 * @param data
	 * @param pkFieldName
	 * @param mapPkFields
	 */
	private static <V, R, Z, W extends Serializable> void doInjectFieldList(List<V> data, String pkFieldName,
			Map<R, Map<Z, List<W>>> mapPkFields) {
		if (mapPkFields != null && mapPkFields.size() > 0) {
			for (V v : data) {
				// 获取当前主表VO对象实例关联外键的值
				R r = ExtRefUtils.getFieldValue(v, pkFieldName);
				Map<Z, List<W>> mapFields = mapPkFields.get(r);
				for (Z rr : mapFields.keySet()) {
					// 从map中取出关联副表的对象实例
					List<W> w = mapFields.get(rr);
					// 使用反射给属性赋值
					ExtRefUtils.setFiledValue(v, rr + "s", w);
				}
			}

		}
	}

}
